/**
 * 对象操作工具类
 */
export class ObjectUtils {
    /**
     * 只将 source 中与 target 同名的属性复制到 target，不添加新属性
     * @param target 目标对象（会被修改）
     * @param source 源对象
     * @returns 修改后的 target
     * @tips 类似于 Object.assign，但只复制已有属性
     * @warn 若 target 为 class 实例，但 class 属性没有初始化，如`class User { id: number }`，
     * 则`new User()`相当于空对象`{}`,因此不会复制任何属性数据到 User 实例中。
     * eg：
     * ```ts
     * ObjectUtils.assignExistingKeys({}, { id: 1, name: 'Alice' }) // {}
     * // 初始化 class 属性，如`class User { id: number = 0 }`或者`class User { id: number = undefined }`
     * ObjectUtils.assignExistingKeys(new User(), { id: 1, name: 'Alice' }) // { id: 1 }
     * ```
     */
    static assignExistingKeys<T extends object>(target: T, source: Record<string, any>): T {
        for (const key of Object.keys(target) as (keyof T)[]) {
            if (key in source) {
                target[key] = source[key as any];
            }
        }
        return target;
    }

    /**
     * 创建一个新对象，只包含 source 中在 target 结构中存在的属性（不修改原对象）
     * @param targetClassOrInstance 目标类（构造函数）或实例
     * @param source 源对象
     * @returns 新对象（普通对象，非实例）
     */
    static pickByStructure<T extends object>(
        targetClassOrInstance: (new () => T) | T,
        source: Record<string, any>
    ): Partial<T> {
        const instance =
            typeof targetClassOrInstance === "function"
                ? new (targetClassOrInstance as new () => T)()
                : targetClassOrInstance;

        const keys = Object.keys(instance) as (keyof T)[];
        const result = {} as Partial<T>;
        for (const key of keys) {
            if (key in source) {
                result[key] = source[key as any];
            }
        }
        return result;
    }

    /**
     * 深度克隆对象（简单版，不处理函数、Date、RegExp、循环引用等复杂情况）
     * 如需完整深拷贝，建议使用 lodash.clonedeep
     */
    static deepClone<T>(obj: T): T {
        if (obj === null || typeof obj !== "object") return obj;
        if (obj instanceof Date) return new Date(obj) as any;
        if (obj instanceof Array) return obj.map(item => this.deepClone(item)) as any;
        if (typeof obj === "object") {
            const cloned = {} as T;
            for (const key in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, key)) {
                    cloned[key] = this.deepClone(obj[key]);
                }
            }
            return cloned;
        }
        return obj;
    }

    /**
     * 判断两个对象是否深度相等（简单实现）
     */
    static deepEqual(a: any, b: any): boolean {
        if (a === b) return true;

        if (a == null || b == null) return false;
        if (typeof a !== "object" || typeof b !== "object") return false;

        const keysA = Object.keys(a);
        const keysB = Object.keys(b);
        if (keysA.length !== keysB.length) return false;

        for (const key of keysA) {
            if (!keysB.includes(key)) return false;
            if (!this.deepEqual(a[key], b[key])) return false;
        }
        return true;
    }

    /**
     * 从对象中移除指定属性（返回新对象）
     */
    static omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
        const result = { ...obj };
        for (const key of keys) {
            delete result[key];
        }
        return result as Omit<T, K>;
    }

    /**
     * 只保留指定属性（返回新对象）
     */
    static pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
        const result = {} as Pick<T, K>;
        for (const key of keys) {
            if (key in obj) {
                result[key] = obj[key];
            }
        }
        return result;
    }

    /**
     * 合并多个对象，但不添加目标对象中不存在的属性（类似 assignExistingKeys 的批量版）
     */
    static mergeExisting<T extends object>(target: T, ...sources: Record<string, any>[]): T {
        for (const source of sources) {
            this.assignExistingKeys(target, source);
        }
        return target;
    }

    /**
     * 检查对象是否为空（无自有可枚举属性）
     */
    static isEmpty(obj: object): boolean {
        return Object.keys(obj).length === 0;
    }
}
